package com.abc.pay.client;

public class Base64 {
    private static final int LOWER_CASE_A_VALUE = 26;
    private static final int ZERO_VALUE = 52;
    private static final int PLUS_VALUE = 62;
    private static final int SLASH_VALUE = 63;
    private static final int SIX_BIT_MASK = 63;
    private static final int EIGHT_BIT_MASK = 255;
    private String mString;
    private int mIndex = 0;

    public Base64() {
    }

    public String encode(byte[] data) {
        int charCount = data.length * 4 / 3 + 4;
        StringBuffer result = new StringBuffer(charCount * 77 / 76);
        int byteArrayLength = data.length;
        int byteArrayIndex = 0;
        int byteTriplet;
        byte b3;
        byte b2;
        byte b1;
        while(byteArrayIndex < byteArrayLength - 2) {
            byteTriplet = this.convertUnsignedByteToInt(data[byteArrayIndex++]);
            byteTriplet <<= 8;
            byteTriplet |= this.convertUnsignedByteToInt(data[byteArrayIndex++]);
            byteTriplet <<= 8;
            byteTriplet |= this.convertUnsignedByteToInt(data[byteArrayIndex++]);
            b3 = (byte)(63 & byteTriplet);
            byteTriplet >>= 6;
            b2 = (byte)(63 & byteTriplet);
            byteTriplet >>= 6;
            b1 = (byte)(63 & byteTriplet);
            byteTriplet >>= 6;
            byte b0 = (byte)(63 & byteTriplet);
            result.append(this.mapByteToChar(b0));
            result.append(this.mapByteToChar(b1));
            result.append(this.mapByteToChar(b2));
            result.append(this.mapByteToChar(b3));
        }
        if (byteArrayIndex == byteArrayLength - 1) {
            byteTriplet = this.convertUnsignedByteToInt(data[byteArrayIndex++]);
            byteTriplet <<= 4;
            b3 = (byte)(63 & byteTriplet);
            byteTriplet >>= 6;
            b2 = (byte)(63 & byteTriplet);
            result.append(this.mapByteToChar(b2));
            result.append(this.mapByteToChar(b3));
            result.append("==");
        }
        if (byteArrayIndex == byteArrayLength - 2) {
            byteTriplet = this.convertUnsignedByteToInt(data[byteArrayIndex++]);
            byteTriplet <<= 8;
            byteTriplet |= this.convertUnsignedByteToInt(data[byteArrayIndex++]);
            byteTriplet <<= 2;
            b3 = (byte)(63 & byteTriplet);
            byteTriplet >>= 6;
            b2 = (byte)(63 & byteTriplet);
            byteTriplet >>= 6;
            b1 = (byte)(63 & byteTriplet);
            result.append(this.mapByteToChar(b1));
            result.append(this.mapByteToChar(b2));
            result.append(this.mapByteToChar(b3));
            result.append("=");
        }
        return result.toString();
    }

    public byte[] decode(String data) {
        this.mString = data;
        this.mIndex = 0;
        int mUsefulLength = 0;
        int length = this.mString.length();
        int byteArrayLength;
        for(byteArrayLength = 0; byteArrayLength < length; ++byteArrayLength) {
            if (this.isUsefulChar(this.mString.charAt(byteArrayLength))) {
                ++mUsefulLength;
            }
        }
        byteArrayLength = mUsefulLength * 3 / 4;
        byte[] result = new byte[byteArrayLength];
        int byteIndex;
        int byteTriplet;
        for(byteIndex = 0; byteIndex + 2 < byteArrayLength; byteIndex += 3) {
            byteTriplet = this.mapCharToInt(this.getNextUsefulChar());
            byteTriplet <<= 6;
            byteTriplet |= this.mapCharToInt(this.getNextUsefulChar());
            byteTriplet <<= 6;
            byteTriplet |= this.mapCharToInt(this.getNextUsefulChar());
            byteTriplet <<= 6;
            byteTriplet |= this.mapCharToInt(this.getNextUsefulChar());
            result[byteIndex + 2] = (byte)(byteTriplet & 255);
            byteTriplet >>= 8;
            result[byteIndex + 1] = (byte)(byteTriplet & 255);
            byteTriplet >>= 8;
            result[byteIndex] = (byte)(byteTriplet & 255);
        }
        if (byteIndex == byteArrayLength - 1) {
            byteTriplet = this.mapCharToInt(this.getNextUsefulChar());
            byteTriplet <<= 6;
            byteTriplet |= this.mapCharToInt(this.getNextUsefulChar());
            byteTriplet >>= 4;
            result[byteIndex] = (byte)(byteTriplet & 255);
        }
        if (byteIndex == byteArrayLength - 2) {
            byteTriplet = this.mapCharToInt(this.getNextUsefulChar());
            byteTriplet <<= 6;
            byteTriplet |= this.mapCharToInt(this.getNextUsefulChar());
            byteTriplet <<= 6;
            byteTriplet |= this.mapCharToInt(this.getNextUsefulChar());
            byteTriplet >>= 2;
            result[byteIndex + 1] = (byte)(byteTriplet & 255);
            byteTriplet >>= 8;
            result[byteIndex] = (byte)(byteTriplet & 255);
        }
        return result;
    }

    private int mapCharToInt(char c) {
        if (c >= 'A' && c <= 'Z') {
            return c - 65;
        } else if (c >= 'a' && c <= 'z') {
            return c - 97 + 26;
        } else if (c >= '0' && c <= '9') {
            return c - 48 + 52;
        } else if (c == '+') {
            return 62;
        } else if (c == '/') {
            return 63;
        } else {
            throw new IllegalArgumentException(c + " is not a valid Base64 character.");
        }
    }

    private char mapByteToChar(byte b) {
        if (b < 26) {
            return (char)(65 + b);
        } else if (b < 52) {
            return (char)(97 + (b - 26));
        } else if (b < 62) {
            return (char)(48 + (b - 52));
        } else if (b == 62) {
            return '+';
        } else if (b == 63) {
            return '/';
        } else {
            throw new IllegalArgumentException("Byte " + new Integer(b) + " is not a valid Base64 value");
        }
    }

    private boolean isUsefulChar(char c) {
        return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '+' || c == '/';
    }

    private char getNextUsefulChar() {
        char result;
        for(result = '_'; !this.isUsefulChar(result); result = this.mString.charAt(this.mIndex++)) {
        }
        return result;
    }

    private int convertUnsignedByteToInt(byte b) {
        return b >= 0 ? b : 256 + b;
    }

    public static Base64 getEncoder(){
        return new Base64();
    }
}
